package com.hundsun.monitor.client.view;

import java.util.HashSet;
import java.util.Iterator;

import com.google.gwt.event.dom.client.HasAllMouseHandlers;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;

public class DockableWidget extends SimplePanel implements MouseMoveHandler,MouseDownHandler,MouseUpHandler{

	private class DragInfo {
		public Widget dragWidget = null;
		public int dragStartX, dragStartY;
		public int centerX, centerY;
	}

	private DragInfo dragInfo = null;
	private DockableWidget switchWith = null;
	private static HashSet dockableContainers = new HashSet();
	private HashSet dockableListeners = new HashSet();

	public DockableWidget(Widget w) {
		setWidget(w);
		setStyleName("z-DockableWidget");
		
		if(w instanceof HasAllMouseHandlers){
			((HasAllMouseHandlers) w).addMouseDownHandler(this);
			((HasAllMouseHandlers) w).addMouseMoveHandler(this);
			((HasAllMouseHandlers) w).addMouseUpHandler(this);
		}
			
	}

	public static void addContainer(Widget widget) {
		dockableContainers.add(widget);
	}

	public void onAttach() {
		super.onAttach();
		dockableContainers.add(getParent());
	}

	protected void findDropContainer(int absCenterX, int absCenterY) {
		Widget parentWidget = getParent();
		for (Iterator itContainers = dockableContainers.iterator(); itContainers.hasNext() && switchWith == null;) {
			Widget containerWidget = (Widget) itContainers.next();
			if (containerWidget instanceof HasWidgets && absCenterX >= containerWidget.getAbsoluteLeft()
					&& absCenterX <= containerWidget.getAbsoluteLeft() + containerWidget.getOffsetWidth() && absCenterY >= containerWidget.getAbsoluteTop()
					&& absCenterY <= containerWidget.getAbsoluteTop() + containerWidget.getOffsetHeight()) {
				if (containerWidget != parentWidget && containerWidget instanceof HasWidgets)
					((HasWidgets) containerWidget).add(this);
				findDropWidget(absCenterX, absCenterY, (HasWidgets) containerWidget);
			}
		}
	}

	protected void findDropWidget(int absCenterX, int absCenterY, HasWidgets container) {
		int width = dragInfo.dragWidget.getOffsetWidth();
		int height = dragInfo.dragWidget.getOffsetHeight();
		for (Iterator it = container.iterator(); it.hasNext() && switchWith == null;) {
			Widget sibling = (Widget) it.next();
			if (sibling instanceof DockableWidget && sibling != this && absCenterX >= sibling.getAbsoluteLeft() && absCenterX <= sibling.getAbsoluteLeft() + width
					&& absCenterY >= sibling.getAbsoluteTop() && absCenterY <= sibling.getAbsoluteTop() + height) {
				switchWith = (DockableWidget) sibling;
				DeferredCommand.addCommand(new Command() {
					public void execute() {
						swapChild();
					}
				});
			}
		}

	}

	public void endDrag(Widget sender) {
		if (dragInfo != null) {
			switchWith = null;
			DOM.setStyleAttribute(dragInfo.dragWidget.getElement(), "position", "");
			DOM.setStyleAttribute(dragInfo.dragWidget.getElement(), "left", "");
			DOM.setStyleAttribute(dragInfo.dragWidget.getElement(), "top", "");
			;
			dragInfo.dragWidget.setWidth("100%");
			setWidget(dragInfo.dragWidget);
			DOM.releaseCapture(sender.getElement());
			dragInfo = null;
			doOnDocked(getWidget());
		}
	}

	public void addDockableListener(DockableListener listener) {
		dockableListeners.add(listener);
	}

	protected void doOnDocked(Widget widget) {
		for (Iterator it = dockableListeners.iterator(); it.hasNext();) {
			DockableListener listener = (DockableListener) it.next();
			listener.onDocked(widget);
		}
	}

	protected void swapChild() {
		if (switchWith != null) {
			Widget childOther = switchWith.getWidget();
			Widget placeholder = getWidget();
			switchWith.setWidget(placeholder);
			setWidget(childOther);
			switchWith.dragInfo = dragInfo;
			Widget childThis = dragInfo.dragWidget;
			dragInfo = null;

			((GadgetContainerView)childThis).removeMouseHandler();
			((HasAllMouseHandlers)childThis).addMouseDownHandler(switchWith);
			((HasAllMouseHandlers)childThis).addMouseMoveHandler(switchWith);
			((HasAllMouseHandlers)childThis).addMouseUpHandler(switchWith);
			if( childOther instanceof HasAllMouseHandlers){
				((GadgetContainerView)childOther).removeMouseHandler();
				((HasAllMouseHandlers) childOther).addMouseDownHandler(this);
				((HasAllMouseHandlers) childOther).addMouseUpHandler(this);
				((HasAllMouseHandlers) childOther).addMouseMoveHandler(this);
			}
			switchWith = null;
		}
	}

	@Override
	public void onMouseUp(MouseUpEvent event) {
		endDrag((Widget) event.getSource());
	}

	@Override
	public void onMouseDown(MouseDownEvent event) {
		Widget sender = (Widget) event.getSource();
		endDrag(sender);
		dragInfo = new DragInfo();
		dragInfo.dragWidget = getWidget();

		int left = dragInfo.dragWidget.getAbsoluteLeft() - 1;
		int top = dragInfo.dragWidget.getAbsoluteTop() - 1;
		int width = dragInfo.dragWidget.getOffsetWidth();
		int height = dragInfo.dragWidget.getOffsetHeight();
		dragInfo.dragWidget.setWidth(width + "px");

		HTML placeholder = new HTML(" ");
		placeholder.setStyleName("z-Placeholder");
		placeholder.setHeight((height + 2) + "px");
		setWidget(placeholder);
		RootPanel.get().add(dragInfo.dragWidget);

		Element elem = dragInfo.dragWidget.getElement();
		DOM.setStyleAttribute(elem, "position", "absolute");
		DOM.setStyleAttribute(elem, "left", left + "px");
		DOM.setStyleAttribute(elem, "top", top + "px");
		DOM.setCapture(sender.getElement());
		dragInfo.dragStartX = event.getX();
		dragInfo.dragStartY = event.getY();
		dragInfo.centerX = width / 2;
		dragInfo.centerY = height / 2;
	}

	@Override
	public void onMouseMove(MouseMoveEvent event) {
		if (dragInfo != null && switchWith == null) {
			int left = event.getX() + dragInfo.dragWidget.getAbsoluteLeft() - dragInfo.dragStartX;
			int top = event.getY() + dragInfo.dragWidget.getAbsoluteTop() - dragInfo.dragStartY;

			Element elem = dragInfo.dragWidget.getElement();
			DOM.setStyleAttribute(elem, "left", left + "px");
			DOM.setStyleAttribute(elem, "top", top + "px");

			findDropContainer(dragInfo.centerX + left, dragInfo.centerY + top);
		}
	}

}
